/**
 * ViewTest.java
 * ************************** 
 * @date Mar 19, 2011
 * @author Michele Tamburini
 * 
 * **************************
 */
package JUnitTests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import gossipServices.basic.nodeDescriptors.ConcreteNodeDescriptor;
import gossipServices.basic.nodeDescriptors.GeneralNodeDescriptor;
import gossipServices.basic.nodeDescriptors.NodeDescriptor;
import gossipServices.basic.nodeDescriptors.comparators.AgeInverseSorterer;
import gossipServices.basic.nodeDescriptors.comparators.AgeSorterer;
import gossipServices.basic.nodeDescriptors.comparators.IdSorterer;
import gossipServices.basic.view.ConcretePartialView;
import gossipServices.basic.view.PartialView;
import gossipServices.basic.view.PartialView.PEER_SELECTION_ENUM;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Vector;

import org.junit.Before;
import org.junit.Test;

/**
 * @author michele
 * 
 * 
 * Description: 
 *
 */
public class ViewTest {
	
	private int RAND_MAX = 25;
	private int SIZE = 10;
	
	private static Vector<NodeDescriptor> desc;
	private static ConcretePartialView view;

	/**
	 * @throws java.lang.Exception
	 */
	@Before
	public void setUp() throws Exception {
		desc = new Vector<NodeDescriptor>();
		for(int i=0; i<SIZE; i++){
			desc.add(new ConcreteNodeDescriptor(""+i,
					InetAddress.getLocalHost(), 
					new Float(Math.random() * RAND_MAX).intValue()));
		}
		
		view = new ConcretePartialView(desc);
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#ConcretePartialView(int)}.
	 */
	@Test
	public void testConcretePartialViewInt() {
		ConcretePartialView v1 = new ConcretePartialView(0);
		ConcretePartialView v2 = new ConcretePartialView(7);
		assertNotNull(v1);
		assertNotNull(v2);
//		fail("Not yet implemented");
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#ConcretePartialView(java.util.List)}.
	 */
	@Test
	public void testConcretePartialViewListOfNodeDescriptor() {
		assertNotNull(view);
		assertTrue(view.size() == desc.size());
		assertTrue(view.head(1).selectPeer(PEER_SELECTION_ENUM.tail).equals(desc.get(0)));
//		fail("Not yet implemented");
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#addNodeDescriptor(gossipServices.basic.nodeDescriptors.ConcreteNodeDescriptor)}.
	 */
	@Test
	public void testAddNodeDescriptor() {
		ConcretePartialView v = new ConcretePartialView(0);
		assertTrue(v.size() == 0);
		try {
			ConcreteNodeDescriptor node = new ConcreteNodeDescriptor("one", InetAddress.getLocalHost(), new Integer(5));
			v.addNodeDescriptor(node);
			assertTrue(v.size() == 1);
			assertTrue(v.head(1).selectPeer(PEER_SELECTION_ENUM.tail).equals(node));
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
//		fail("Not yet implemented");
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#append(gossipServices.basic.view.PartialView)}.
	 */
	@Test
	public void testAppend() {
		int appendNum = 3;
		int oldSize = view.size();
		Vector<NodeDescriptor> toAppend = new Vector<NodeDescriptor>();
		Vector<ConcreteNodeDescriptor> testVector = new Vector<ConcreteNodeDescriptor>();
		try {
			for(int i=0; i<appendNum; i++){
				toAppend.add(new ConcreteNodeDescriptor(""+SIZE+"_"+i, InetAddress.getLocalHost(), new Integer(30)));
				testVector.add(new ConcreteNodeDescriptor(""+SIZE+"_"+i, InetAddress.getLocalHost(), new Integer(30)));
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		System.out.println("BEFORE APPEND:");
		showView(view);
		PartialView buffer = new ConcretePartialView(toAppend);
		view.append(buffer);
		System.out.println("APPEND AFTER:");
		showView(view);
		
		assertTrue(buffer.size() == appendNum);
		assertNotNull(buffer);
		assertTrue(testVector.size() == appendNum);
		assertTrue(view.size() == oldSize + appendNum);
		Vector<ConcreteNodeDescriptor> vec = view.getDescriptors();
		for(int i=0; i<appendNum; i++){
			assertTrue(testVector.get(i).equals(vec.get(vec.size()-appendNum+i)));
		}
//		fail("Not yet implemented");
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#head(int)}.
	 */
	@Test
	public void testHead() {
		int extracted = 3;
		PartialView v2 =  view.head(extracted);
		assertTrue(v2.size() == extracted);
		NodeDescriptor node = view.head(1).selectPeer(PEER_SELECTION_ENUM.tail);
		assertNotNull(node);
		NodeDescriptor node2 = v2.head(1).selectPeer(PEER_SELECTION_ENUM.tail);
		assertNotNull(node2);
		assertTrue(node.equals(node2));
		v2.removeHead(extracted);
		assertTrue(v2.size() == 0);
		assertNotNull(v2);
		
		int oldSize = view.size();
		PartialView all = view.head(view.size());
		assertEquals(oldSize, all.size());
		
		assertNotNull(view.head(0));
		
		print("****************************************");
		print("\nTestHead:\nview:");
		print(view);
		
		// test an empty view:
		// try to remove the first element from the head in an empty view
		PartialView emptyView = new ConcretePartialView(0);
		assertEquals(0, emptyView.head(1).size());
		
//		fail("Not yet implemented");
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#increaseAge()}.
	 */
	@Test
	public void testIncreaseAge() {
		ConcretePartialView oldView = (ConcretePartialView) view.head(view.size());
		
		System.out.println("INCREASE AGE BEFORE:");		
		showView(view);
		view.increaseAge();
		System.out.println("INCREACE AGE AFTER:");
		showView(view);
		
		Vector<ConcreteNodeDescriptor> oldVec = oldView.getDescriptors();
		Vector<ConcreteNodeDescriptor> vec = view.getDescriptors();
		for(int i=0; i<view.size(); i++){
			assertTrue(vec.get(i).getAge().intValue() - 1 == oldVec.get(i).getAge().intValue());
		}
//		fail("Not yet implemented");
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#permute(Int)}.
	 */
	@Test
	public void testPermute() {
		int permutedElements = 3;
		Vector<ConcreteNodeDescriptor> oldVector = (Vector<ConcreteNodeDescriptor>) view.getDescriptors().clone();
		Vector<ConcreteNodeDescriptor> sortedVector = (Vector<ConcreteNodeDescriptor>) view.getDescriptors().clone();
		Comparator<ConcreteNodeDescriptor> reverse = Collections.reverseOrder();
//		Collections.sort(sortedVector, reverse);
		Collections.sort(sortedVector, new AgeInverseSorterer());
		List<ConcreteNodeDescriptor> shiftedElements = sortedVector.subList(0, permutedElements);
		
		System.out.println("PERMUTE BEFORE:");		
		showView(view);
		view.permute(permutedElements);
		System.out.println("PERMUTE AFTER:");
		showView(view);
		
		int oldAge = 0 ;
		Vector<ConcreteNodeDescriptor> vec = view.getDescriptors();
		//This is a test for only the sorting.... we need a more detailed one...
//		for(int i=0; i<view.size(); i++){
//			assertTrue(oldAge <= vec.get(i).getAge().intValue());
//			oldAge = vec.get(i).getAge().intValue();
//		}
		
		//verify that permutedElements are at the end of the view 
		for(int i=0; i<permutedElements; i++){
			assertTrue(sortedVector.get(i).equals(vec.get( vec.size()-i-1 ) ) == true);
//			System.out.println("expected: "+sortedVector.get(i)+" actual: "+vec.get(vec.size()-i));
			assertEquals(sortedVector.get(i), vec.get(vec.size()-i-1));
		}
		
		//check that all other elements remain in the same earlier order
		System.out.println("Check position of element in the actual view:\n" +
				"if not in the H oldest elements they have to be at the same earlier positions");
		for(int i=0, j=0; i<vec.size() - permutedElements; i++){
			ConcreteNodeDescriptor nodeVec = vec.get(i);
			ConcreteNodeDescriptor nodeOld = oldVector.get(j);
			while (shiftedElements.contains(nodeOld)){
				j++;
				nodeOld = oldVector.get(j);
			}
//			assertTrue(nodeVec.equals(nodeOld) == true);
			System.out.println("expected: "+nodeOld+" actual: "+nodeVec);
			assertEquals(nodeOld, nodeVec);
			j++;
		}
		
		
//		fail("Not yet implemented");
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#removeDuplicates()}.
	 */
	@Test
	public void testRemoveDuplicates() {
		Vector<NodeDescriptor> vec = new Vector<NodeDescriptor>();
		ConcretePartialView temp;
		for(int i=0; i<view.size(); i++){
			if( i%2 == 0 ){
				try {
					ConcreteNodeDescriptor node = new ConcreteNodeDescriptor(""+i, 
							InetAddress.getLocalHost(), 
							new Float(Math.random() * RAND_MAX).intValue());
					vec.add(node);
				} catch (UnknownHostException e) {
					e.printStackTrace();
				}
			}
		}
		temp = new ConcretePartialView(vec);
		view.append(temp);
		
		System.out.println("DUPLICATE REMOVAL BEFORE:");		
		showView(view);
		view.removeDuplicates();
		System.out.println("DUPLICATE REMOVAL AFTER:");
		showView(view);
		
		Vector<ConcreteNodeDescriptor> vec2 = view.getDescriptors();
		Vector<ConcreteNodeDescriptor> testVec = new Vector<ConcreteNodeDescriptor>();
		for(ConcreteNodeDescriptor node : vec2 ){
			assertFalse(testVec.contains(node));
			testVec.add(new ConcreteNodeDescriptor(node.getNodeName(), node.getNodeAddress(), node.getAge()));
		}
		
//		fail("Not yet implemented");
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#removeHead(int)}.
	 */
	@Test
	public void testRemoveHead() {
		ConcretePartialView oldView = (ConcretePartialView) view.head(view.size());
		int removed = 3;
		
		System.out.println("BEFORE REMOVAL");
		showView(view);
		
		view.removeHead(removed);
		assertTrue(oldView.size() != view.size());
		
		NodeDescriptor node = view.head(1).selectPeer(PEER_SELECTION_ENUM.tail);
		assertNotNull(node);
		NodeDescriptor node2 = oldView.head(1).selectPeer(PEER_SELECTION_ENUM.tail);
		assertNotNull(node2);
		assertTrue(!(node.equals(node2)));
		
		System.out.println("AFTER REMOVAL OF "+removed+" ELEMENTS");
		showView(view);
		
//		fail("Not yet implemented");
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#removeOldItems(int, int)}.
	 */
	@Test
	public void testRemoveOldItems() {
		ConcretePartialView oldView = (ConcretePartialView) view.head(view.size());
		Vector<ConcreteNodeDescriptor> oldVec = oldView.getDescriptors();
		Comparator reverter = Collections.reverseOrder();
//		Collections.sort(oldVec,reverter);
		Collections.sort(oldVec, new AgeInverseSorterer());
		
		int removed = 4;
		System.out.println("BEFORE THE REMOVAL OF OLD ITEMS");
		showView(view);
		view.removeOldItems(removed, SIZE-removed);
		System.out.println("AFTER THE REMOVAL OF THE "+removed+" OLDEST ITEMS");
		showView(view);
		
		Vector<ConcreteNodeDescriptor> vec = view.getDescriptors();
		for(int i=0; i<removed; i++){
			 assertTrue(!vec.contains(oldVec.get(i)));
		}
		
//		fail("Not yet implemented");
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#select(int, int, int, gossipServices.basic.view.PartialView)}.
	 */
	@Test
	public void testSelect() {		
		Vector<NodeDescriptor> vec = new Vector<NodeDescriptor>();
		Vector<ConcreteNodeDescriptor> oldViewVec = view.getDescriptors();
		NodeDescriptor myDescriptor = new GeneralNodeDescriptor("pippo");
		boolean retSelect = false;
		
		try {
			for(int i=0; i<(SIZE/2 -1); i++){
				vec.add(new GeneralNodeDescriptor(""+i,
						InetAddress.getLocalHost(), 
						new Float(Math.random() * RAND_MAX).intValue()));
			}
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		view.getDescriptors().add((ConcreteNodeDescriptor) myDescriptor);
		ConcretePartialView bufferp = new ConcretePartialView(vec);
		Vector<ConcreteNodeDescriptor> bufferpVec = bufferp.getDescriptors();
		
		
		System.out.println("****************\n" +
		"BEFORE SELECT:");
		System.out.println("VIEW:");
		showView(view);
		System.out.println("BUFFERp:");
		showView(bufferp);
		
		retSelect = view.select(SIZE, 3, 3, bufferp, myDescriptor);
		System.out.println("AFTER SELECT:");
		showView(view);
		
		//the view must be at the same original size
		assertEquals(SIZE, view.size());
		
		
		//the node must keep only the freshest entries:
		Vector<ConcreteNodeDescriptor> vecView = view.getDescriptors();
		
		if(retSelect == true)
			assertTrue(vecView.contains(myDescriptor) == false);
			
		for(int i=0; i<bufferp.size(); i++){
			ConcreteNodeDescriptor buf = bufferpVec.get(i);
			int target = vecView.indexOf(buf);
			assertTrue(buf.getAge() >= vecView.get(target).getAge());
		}
//		fail("Not yet implemented");
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#selectPeer(gossipServices.basic.view.PartialView.PEER_SELECTION_ENUM)}.
	 */
	@Test
	public void testSelectPeer() {
		Vector<ConcreteNodeDescriptor> vec = view.getDescriptors();
		assertTrue(view.selectPeer(PEER_SELECTION_ENUM.rand) != null);
		assertTrue(view.selectPeer(PEER_SELECTION_ENUM.tail).equals(vec.get(vec.size()-1)));
		
//		fail("Not yet implemented");
	}
	
	@Test
	public void testRemoveNodeDescriptor(){
		boolean ret;
		ConcreteNodeDescriptor myDescriptor = new ConcreteNodeDescriptor("pippo");
		System.out.println("TEST REMOVE NODE DESCRIPTOR");
		
		ret = view.removeNodeDescriptor(SIZE, myDescriptor);
		assertTrue(view.size() == SIZE);
		System.out.println("1) return: "+ret);
		
		ret = view.removeNodeDescriptor(SIZE, view.getDescriptors().get(0));
		assertTrue(view.size() == SIZE);
		System.out.println("2) return: "+ret);
		
		ret = view.addNodeDescriptor(myDescriptor);
		System.out.println("added my descriptor: "+ret);
		if(ret == true)
			assertTrue(view.size() == SIZE + 1);
		
		ret = view.removeNodeDescriptor(SIZE, myDescriptor);
		System.out.println("3) return: "+ret);
		assertTrue(view.getDescriptors().contains(myDescriptor) == false);
		
	}
	
	@Test
	public void testMerge(){
		String addingNames[] = {"add1", "add2", "add3"};
		Vector<NodeDescriptor> adding = new Vector<NodeDescriptor>();
		Vector<NodeDescriptor> nodeTest = new Vector<NodeDescriptor>();
		PartialView addingView;
		for(int i=0; i<addingNames.length; i++){
			adding.add(new ConcreteNodeDescriptor(addingNames[i]));
			nodeTest.add(new ConcreteNodeDescriptor(addingNames[i]));
		}
		adding.addAll(desc);
		
		Vector<ConcreteNodeDescriptor> vec = view.getDescriptors();
		//verifying that all new elements are not actually into the view
		for(NodeDescriptor n : nodeTest){
			assertTrue(!vec.contains(n));
		}
		//check the size of the adding PartialView
		assertEquals(desc.size() + nodeTest.size(), adding.size());
		
		print("TEST: merge()");
		addingView = new ConcretePartialView(adding);
		assertEquals(adding.size(), addingView.size());
		print("before test");
		viewPrinting();
		view.merge(addingView);
		print("after merge:\n"+view);
		
		vec = view.getDescriptors();
		for(NodeDescriptor n : nodeTest){
			assertTrue(vec.contains(n));
		}
		
		//check that all the elements are contained only once into the new view
		int occurrences[] = new int[vec.size()];
		for(int h: occurrences){
			h = 0;
		}
		// n^2 power!!!
		for(int i=0; i<vec.size(); i++){
			for(int j=0; j<vec.size(); j++){
				if(vec.get(i).equals(vec.get(j)))
					occurrences[i]++;
			}
		}
		for(int h: occurrences){
			assertEquals(1, h);
		}
		
				
	}
	
	@Test
	public void testMergeWithDuplicatesAndOrdering(){
		/* the merge function has to preserve all NodeDescriptor(s) 
		ordering (in particular removing the duplicates...) */
		String addingNames[] = {"5", "adding1", "pippo", "4", "1"};
		Vector<NodeDescriptor> addingVec = new Vector<NodeDescriptor>();
		Vector<NodeDescriptor> oldVec = (Vector<NodeDescriptor>) view.getDescriptors().clone();
		Vector<ConcreteNodeDescriptor> newVec;
		PartialView mergingView;
		
		for(String s : addingNames){
			addingVec.add(new ConcreteNodeDescriptor(s));
		}
		
		mergingView = new ConcretePartialView(addingVec);
		
		print("TEST: ordering and duplicates of the merge");
		print("before merge, view:\n"+view);
		view.merge(mergingView);
		print("adding\n"+mergingView);
		print("afterMergeView:\n"+view);
		
		newVec = view.getDescriptors();
		
		/*
		 * Keeping the initial ordering means that the old view is
		 * completely preserved and only new NodeDescriptors are 
		 * appended.
		 */
		for(NodeDescriptor oldNode : oldVec){
			assertTrue(newVec.contains(oldNode));
		}
		for(NodeDescriptor addingNode : addingVec){
			assertTrue(newVec.contains(addingNode));
		}
			
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#size()}.
	 */
	@Test
	public void testSize() {
		assertTrue(view.size() == SIZE);
//		fail("Not yet implemented");
	}
	
	private void print(Object o){
		System.out.println(o);
	}

	/**
	 * Test method for {@link gossipServices.basic.view.ConcretePartialView#removeAtRandom(int)}.
	 */
	@Test
	public void testRemoveAtRandom() {
		int removed = 6;
		view.removeAtRandom(6);
		assertTrue(view.size() == SIZE - removed);
//		fail("Not yet implemented");
	}
	
	@Test
	public void testApplyComparator(){
		view.applyComparator(new AgeSorterer());
		
		ConcreteNodeDescriptor prec = view.getDescriptors().firstElement();
		for(ConcreteNodeDescriptor n : view.getDescriptors()){
			assertTrue(prec.getAge() <= n.getAge());
		}
		
		view.applyComparator(new IdSorterer());
		prec = view.getDescriptors().firstElement();
		for(ConcreteNodeDescriptor n : view.getDescriptors()){
			assertTrue(prec.getNodeID()<= n.getNodeID());
		}
	}
	
	@Test
	public void viewPrinting(){
		System.out.println("PRINTING VIEW TEST:");
		System.out.println(view);
	}
	
	private void showView(ConcretePartialView v){
		Vector<ConcreteNodeDescriptor> tempDescVec;		
		tempDescVec = v.getDescriptors();
		for(ConcreteNodeDescriptor n : tempDescVec){
			System.out.println(n);
		}
	}

}
